from pyecharts.charts import Map, Timeline, Bar, Grid
from pyecharts import options as opts
import csv
import copy


province_dict = {
    '北京', '天津', '河北', '河南', '辽宁', '吉林', '黑龙江', '山东',
    '江苏', '上海', '浙江', '安徽', '福建', '江西', '广东', '广西', '海南',
    '湖南', '湖北', '山西', '内蒙古', '宁夏', '青海', '陕西', '甘肃',
    '新疆', '四川', '重庆', '贵州', '云南', '西藏', '香港', '澳门', '台湾'
}

maxNum = 68135
minNum = 0


def get_daily_data(file_path: str):
    """
    疫情数据整理，读入数据，整理成字典
    daily_data:
    {'2020/1/11': [['湖北', 41]],
    '2020/1/18': [['湖北', 4]],
    '2020/1/19': [['湖北', 17]],
    '2020/1/20': [['北京', 5], ['广东', 14], ['湖北', 136], ['上海', 1]],
    }
    :param file_path:数据文件路径
    :return: 字典，每日每省疫情汇总
    """
    with open(file_path) as file:
        reader = csv.reader(file)
        temp_data = list(reader)[1:]

    # 以日期为key，[省份，确诊数]为value，生成确诊数字典
    daily_data = {}
    for each in temp_data:
        date = each[0]
        if date in daily_data:
            # data[date].append([{
            #     'name': each[1],
            #     'value': int(each[2])
            # }])
            daily_data[date].append([each[1], int(each[2])])
        else:
            # data[date] = [{
            #     'name': each[1],
            #     'value': int(each[2])
            # }]
            daily_data[date] = [[each[1], int(each[2])]]
    # 当日无确诊数据省份用0补上数据
    for i in daily_data:
        day_data = daily_data[i]
        province_list = []
        # 得到一日有确诊的省份列表
        for j in range(len(day_data)):
            province_list.append(day_data[j][0])
        # 确诊省份列表和省份字典进行比较，未出现的省份数据用0补上
        for k in province_dict:
            if k not in province_list:
                daily_data[i].append([k, 0])
    # 每日新增按省份名称排序（便于累加处理）
    for d in daily_data:
        daily_data[d] = sorted(daily_data[d], key=lambda x: x[0])
    return daily_data


def get_total_data(data: dict):
    """
    生成各省累计确诊统计数据
    :param data: get_daily_data生成的每日确诊数据字典
    :return: 累计确诊数据字典
    """
    # 深拷贝data，防止覆盖每日新增数据字典
    test = copy.deepcopy(data)
    # 生成各省累计确诊数字典
    total_data = {}
    # 先把第一日数据存入字典
    total_data['2020/1/11'] = test['2020/1/11']
    date_list = list(test.keys())
    # 将前一日的数据加到后一日的数据上
    for d in range(len(date_list)-1):
        day_first_data = test[str(date_list[d])]
        day_next_data = test[str(date_list[d+1])]
        temp_total = []
        for m in range(34):
            # for n in range(34):
            #     if day_first_data[m][0] == day_next_data[n][0]:
            #         day_next_data[n][1] += day_first_data[m][1]
            #         temp_total.append([day_next_data[n][0], day_next_data[n][1]])
            #         # province = str(day_first_data[m][0])
            #         # sum = int(day_next_data[n][1]) + int(day_first_data[m][1])
            #         # temp_total.append([province, sum])
            day_next_data[m][1] += day_first_data[m][1]
            temp_total.append(([day_next_data[m][0], day_next_data[m][1]]))

        # 将结果按总确诊数降序排序
        temp_total = sorted(temp_total, key=lambda x: x[1], reverse=True)
        total_data[str(date_list[d+1])] = temp_total
    return total_data


def get_date_list(data: dict):
    """
    生成日期列表
    data_list:['2020/1/11', '2020/1/18', '2020/1/19', '2020/1/20', '2020/1/21', '2020/1/22', '2020/1/23', ……]
    :param data: 字典，每日疫情数据汇总
    :return: 列表，日期汇总
    """
    date_list = list(data.keys())
    return date_list


def get_chart(day: str, daily_data: dict, total_data: dict):
    """
    生成图表
    :param total_data:
    :param daily_data:
    :param day: 日期，生成每一日的疫情数据图表
    :param data: 对应日期的疫情数据
    :return: pyecharts图表
    """
    # 地图显示每日新增，map_data为地图生成所用数据
    map_data = [[d[0], d[1]] for d in daily_data[day] if day in daily_data]
    """
    地图所需一日数据map_data格式：
    [['广东', 3], ['湖北', 72], ['上海', 5], ['天津', 2], ['浙江', 5], ['台湾', 1], 
    ['河南', 1], ['重庆', 5], ['四川', 1], ['北京', 5], ['云南', 1], ['山东', 1]]
    """
    # 生成每日新增疫情地图
    map_chart = (
        Map()
        .add(
            series_name="",
            data_pair=map_data,
            maptype="china",
            zoom=1,
            is_roam=False
        )
        .set_global_opts(
            # 标题配置项
            title_opts=opts.TitleOpts(
                title="中国新冠疫情数据可视化  By:S320060030 王祉元",
                subtitle="每日新增确诊地图 " + "日期:" + str(day),
                pos_left="center",
                pos_top="top",
                title_textstyle_opts=opts.TextStyleOpts(
                    font_size=25, color="rgba(0,0,0,0.9)"
                ),
                subtitle_textstyle_opts=opts.TextStyleOpts(
                    font_size=20, color="rgba(0,0,0,0.9)"
                ),
            ),
            # 视觉映射配置项
            visualmap_opts=opts.VisualMapOpts(
                max_=9999,
                is_piecewise=True,
                pieces=[{"max": 0, "label": "0", "color": "#FFF0F5"},
                        {"max": 9, "min": 1, "label": "1-9", "color": "#FFE0D1"},
                        {"max": 99, "min": 10, "label": "10-99", "color": "#FF7F50"},
                        {"max": 499, "min": 100, "label": "100-499", "color": "#F08080"},
                        {"max": 999, "min": 500, "label": "500-999", "color": "#CD5C5C"},
                        {"max": 9999, "min": 1000, "label": "1000-9999", "color": "#B22222"},
                        {"min": 10000, "label": ">=10000", "color": "#800000"}]
            ),
            # 提示框配置项
            tooltip_opts=opts.TooltipOpts(
                formatter="{b}:{c}"
            ),
        )
    )

    # 条形图显示累计确诊，bar_data_x，bar_data_y为条形图生成所用数据
    bar_data_x = [d[0] for d in total_data[day] if day in total_data]
    bar_data_y = [{"name":d[0], "value":d[1]} for d in total_data[day] if day in total_data]
    """
    条形图所需累计数据bar_data_x，bar_data_y格式：
    bar_data_x:['湖北', '香港', '广东', '河南', '浙江', '湖南', '安徽', '黑龙江',  ……]
    bar_data_y:[68135, 3151, 1678, 1276, 1270, 1019, 991, 947, 933,  ……]
    """
    # 生成累计确诊条形图
    bar_chart = (
        Bar()
        .add_xaxis(xaxis_data=bar_data_x)
        .add_yaxis(
            series_name="",
            yaxis_data=bar_data_y,
            label_opts=opts.LabelOpts(
                is_show=True, position="right", formatter="{b}:{c}"
            )
        )
        .reversal_axis()
        .set_global_opts(
            xaxis_opts=opts.AxisOpts(
                max_=maxNum, axislabel_opts=opts.LabelOpts(is_show=False)
            ),
            yaxis_opts=opts.AxisOpts(
                axislabel_opts=opts.LabelOpts(is_show=False)
            ),
            visualmap_opts=opts.VisualMapOpts(
                is_calculable=True,
                dimension=0,
                pos_left="30",
                pos_top="30",
                textstyle_opts=opts.TextStyleOpts(color="#000"),
                is_piecewise=True,
                pieces=[{"max": 0, "label": "0", "color": "#FFF0F5"},
                        {"max": 9, "min": 1, "label": "1-9", "color": "#FFE0D1"},
                        {"max": 99, "min": 10, "label": "10-99", "color": "#FF7F50"},
                        {"max": 499, "min": 100, "label": "100-499", "color": "#F08080"},
                        {"max": 999, "min": 500, "label": "500-999", "color": "#CD5C5C"},
                        {"max": 9999, "min": 1000, "label": "1000-9999", "color": "#B22222"},
                        {"min": 10000, "label": ">=10000", "color": "#800000"}],
            ),
            title_opts=opts.TitleOpts(
                subtitle="各省累计确诊数据 " + "日期:" + str(day),
                subtitle_textstyle_opts=opts.TextStyleOpts(
                    font_size=20, color="rgba(0,0,0,0.9)"
                ),
                pos_left="20",
                pos_top="43%"
            )
        )

    )

    grid_chart = (
        Grid()
        .add(bar_chart, grid_opts=opts.GridOpts(pos_left="10", pos_right="45%", pos_top="50%", pos_bottom="8%"))
        .add(map_chart, grid_opts=opts.GridOpts())
        # 先加载条形图再加载地图才能不报错
    )

    return grid_chart


if __name__ == '__main__':
    path = 'province_data.csv'
    covid_daily_data = get_daily_data(path)
    covid_total_data = get_total_data(covid_daily_data)
    time_list = get_date_list(covid_daily_data)

    time_line = Timeline(
        init_opts=opts.InitOpts(
            width="1500px", height="700px"
        )
    )
    for day in time_list:
        chart = get_chart(day, covid_daily_data, covid_total_data)
        time_line.add(chart, time_point=str(day))

    time_line.add_schema(
        orient="horizontal",
        symbol="pin",
        is_auto_play=False,
        is_loop_play=False,
        play_interval=300,
        pos_bottom="bottom",
        label_opts=opts.LabelOpts(is_show=True, color="#000", position="bottom"),
    )

    time_line.render("covid_data.html")
